/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.editor.view;

import java.io.IOException;
import java.io.StringReader;
import javax.swing.text.StyleConstants;
import javax.xml.parsers.SAXParserFactory;
import offset.nodes.client.editor.model.DummyContentHandler;
import offset.nodes.client.editor.model.XmlUtils;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
 *
 * @author Walter Lütgenau
 */
public class StyledXMLWriter extends DummyContentHandler implements ContentHandler {

    StyledDocumentWriter writer;
    boolean afterStartElement = false;
    int indent = 0;
    StringBuilder buf = new StringBuilder();
    boolean atStart = true;

    public StyledXMLWriter(StyledDocumentWriter writer) {
        this.writer = writer;
    }

    public StyledXMLWriter(StyledDocumentWriter writer, String input) throws SAXException {
        this.writer = writer;
        parse(input);
    }

    public void parse(String input) throws SAXException {
        try {
            XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
            reader.setContentHandler(this);
            reader.setProperty("http://xml.org/sax/properties/lexical-handler", this);
            reader.parse(new InputSource(new StringReader(input)));
        } catch (Exception e) {
            throw new SAXException(e);
        }
    }

    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_ELEMENT);
        if (afterStartElement)
            write(">");
        if (indent <= 1 && !atStart)
            writeln();
        writeIndent();
        write("<" + getName(uri, localName, qName));
        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_NORMAL);
        indent++;
        buf = new StringBuilder();
        afterStartElement = true;

        writeAttributes(atts);
    }

    public void endElement(String uri, String localName, String qName) throws SAXException {
        indent--;
        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_ELEMENT);
        if (afterStartElement)
            write("/>");
        else {
            writeText();
            StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_ELEMENT);
            writeIndent();
            write("</" + getName(uri, localName, qName) + ">");
        }
        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_NORMAL);

        buf = new StringBuilder();
        afterStartElement = false;
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        if (afterStartElement)
            write(">");
        buf.append(ch, start, length);
        if (!isSpace(buf.toString()))
            afterStartElement = false;
    }

    public void comment(char[] ch, int start, int length) throws SAXException {
        if (afterStartElement)
            write(">");
        afterStartElement = false;

        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_ATTRIBUTE);
        writeIndent();
        write("<!--" + new String(ch, start, length).trim() + "-->");
        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_NORMAL);
    }

    public void writeText() throws SAXException {
        String text = buf.toString();
        if (isSpace(text))
            return;

        if (afterStartElement)
            write(">");

        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_TEXT);
        indent++;
        writeIndent();
        indent--;
        write(buf.toString().trim());
        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_NORMAL);

        afterStartElement = false;
    }

    protected boolean isSpace(String value) {
        for (int i = 0; i < value.length(); i++) {
            if (!Character.isWhitespace(value.charAt(i)))
                return false;
        }
        return true;
    }

    protected void writeAttributes(Attributes atts) throws SAXException {
        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_ATTRIBUTE);
        for (int i = 0; i < atts.getLength(); i++) {
            String name = atts.getQName(i);
            String value = atts.getValue(i);

            write(" " + name + "=\"" + XmlUtils.attributeValueToXML(value) + "\"");
        }
        StyleConstants.setForeground(writer.getAttributes(), StyledHTMLWriter.COLOR_ELEMENT);
    }

    protected void writeIndent() throws SAXException {
        if (!atStart)
            writeln();
        atStart = false;

        for (int i = 0; i < indent; i++) {
            write("\t");
        }
    }

    protected void writeln() throws SAXException {
        write("\n");
    }

    protected void write(String value) throws SAXException {
        try {
            writer.write(value.toCharArray(), 0, value.length());
        } catch (IOException ex) {
            throw new SAXException(ex);
        }
    }

    protected String getName(String uri, String localName, String qName) {
        return qName;
    }
}
